package com.alipay.service.impl;

import com.alipay.constants.AuthenticationConstants;
import com.alipay.exception.BalanceException;
import com.alipay.exception.MqProduceException;
import com.alipay.mapper.AccountOperationMapper;
import com.alipay.mapper.TransferTaskMapper;
import com.alipay.model.dto.TransDto;
import com.alipay.model.entity.AccountEntity;
import com.alipay.model.entity.TransTaskEntity;
import com.alipay.model.enums.DirectionEnum;
import com.alipay.model.enums.QueueEnum;
import com.alipay.model.enums.TaskStatusEnum;
import com.alipay.service.AccountService;
import com.alipay.util.JsonUtil;
import com.alipay.util.MD5Util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

/**
 * @author Jun
 */
@Slf4j
@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private TransferTaskMapper transferTaskMapper;

    @Autowired
    private AccountOperationMapper accountOperationMapper;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Override
    public boolean signCheck(String sign, String transferInfo) {
        if (StringUtils.isEmpty(sign)){
            return false;
        }
        return sign.equals(MD5Util.MD5(AuthenticationConstants.PRI_KEY + transferInfo));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean decrease(TransDto transDto) throws BalanceException {
        TransTaskEntity transTaskEntity = new TransTaskEntity();
        BeanUtils.copyProperties(transDto, transTaskEntity);

        try {
            // 创建本地任务表，记录消息发送状态,与业务表在同一个事务中，这个地方只要有记录就可以认为是扣减成功
            transTaskEntity.setAccountId(transDto.getFromAccount());
            transTaskEntity.setDirection(DirectionEnum.DECREASE.getValue());
            transTaskEntity.setStatus(TaskStatusEnum.SUCCESS.getCode());
            transTaskEntity.setRemark(transDto.getFromAccount() + "-->" + transDto.getToAccount());
            transferTaskMapper.insert(transTaskEntity);
        } catch (DuplicateKeyException e) {
            // 重复扣款，幂等设计，直接返回
            log.error("decrease Concurrency preservation failed , the id is {}", transDto.getTransId());
            return true;
        }

        // 查账户余额
        AccountEntity accountEntity = accountOperationMapper.getBalanceByPayAccountId(transDto.getFromAccount());
        if (accountEntity.getAmount().compareTo(transDto.getAmount()) < 0) {
            // 余额不足
            throw new BalanceException("not have enough money！");
        }

        // 扣款(versionId + 1), 乐观锁控制
        accountOperationMapper.decrease(transDto.getFromAccount(), transDto.getAmount(), accountEntity.getVersionId());
        // 发送MQ,发送不成功的时候事务会回滚，发送成功的话，事务提交
        try {
            // 发送消息队列，用于处理B的入款，这里需要用MQ的ack确认机制，保证消息发送成功
            rabbitTemplate.convertAndSend(
                    QueueEnum.INCREASE_QUEUE.getName(),
                    JsonUtil.toJson(transDto));
        } catch (Exception e) {
            log.error("sendMessage message error, the param transId is {}, exception is ", transDto.getTransId(), e);
            throw new MqProduceException("send message Error");
        }
        return true;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean increase(TransDto transDto) {
        TransTaskEntity transTaskEntity = new TransTaskEntity();
        BeanUtils.copyProperties(transDto, transTaskEntity);
        transTaskEntity.setAccountId(transDto.getToAccount());
        transTaskEntity.setDirection(DirectionEnum.INCREASE.getValue());
        transTaskEntity.setRemark(transDto.getFromAccount() + "-->" + transDto.getToAccount());
        try {
            // 创建本地任务表，记录消息发送状态，与业务表在同一个事务中
            transTaskEntity.setStatus(TaskStatusEnum.SUCCESS.getCode());
            transferTaskMapper.insert(transTaskEntity);
        } catch (DuplicateKeyException e) {
            // 重复入款，幂等设计
            log.error("decrease Concurrency preservation failed , the id is {}", transDto.getTransId());
            return true;
        }

        // 查询账户余额
        AccountEntity accountInfo = accountOperationMapper.getBalanceByPayAccountId(transDto.getToAccount());

        // 此处模拟一个业务场景的失败，需要给A账户回执，需要A进行逆向操作的
        /*
        if ("风险账户".equals(accountInfo.getAccountId())) {
            throw new RiskAccountException("风险账户");
        }*/

        // 收款(versionId + 1), 乐观锁控制
        accountOperationMapper.increase(transDto.getToAccount(), transDto.getAmount(), accountInfo.getVersionId());
        return true;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean revertDecrease(TransDto transDto) {

        // 查询账户余额
        AccountEntity accountInfo = accountOperationMapper.getBalanceByPayAccountId(transDto.getFromAccount());

        // 给A增加余额
        // 收款(versionId + 1), 乐观锁控制
        accountOperationMapper.increase(transDto.getFromAccount(), transDto.getAmount(), accountInfo.getVersionId());

        // 更新任务表的状态为业务失败，后续看业务场景，看此类是否细分补偿
        transferTaskMapper.update(transDto.getFromAccount(), transDto.getTransId(), TaskStatusEnum.SUCCESS.getCode(), TaskStatusEnum.FAILED.getCode());

        return true;
    }
}
